package com.flying.invest.invest.service;

import java.util.Date;
import java.util.List;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.context.ServiceContext;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.security.Principal;
import com.flying.framework.service.AbstractService;
import com.flying.invest.account.service.AccountService;
import com.flying.invest.base.service.CurrencyService;
import com.flying.invest.invest.TransactionType;
import com.flying.invest.invest.model.Investment;
import com.flying.invest.invest.model.InvestmentSuggestion;
import com.flying.invest.invest.model.InvestmentTransaction;
import com.flying.invest.invest.model.InvestmentTransaction.Method;
import com.flying.invest.invest.model.InvestmentTransaction.Objective;
import com.flying.invest.invest.model.InvestmentTransaction.Status;
import com.flying.invest.strategy.model.Strategy;
import com.flying.invest.strategy.service.StrategyService;

@Service(value="InvestmentTransactionService", desc="")
public class InvestmentTransactionService extends AbstractService{
	private InvestmentService vs;
	private AccountService as;
	private StrategyService ss;
	private InvestmentSuggestionService iss;
	private CurrencyService cs;
	public void setInvestmentService(InvestmentService s) {
		this.vs = s;
	}
	public void setAccountService(AccountService s) {
		this.as = s;
	}
	public void setStrategyService(StrategyService s) {
		this.ss = s;
	}
	public void setInvestmentSuggestionService(InvestmentSuggestionService s) {
		this.iss = s;
	}
	public void setCurrencyService(CurrencyService s) {
		this.cs = s;
	}
        
	@MethodInfo("新增")
	@DaoCreate(entity=InvestmentTransaction.ENTITY_NAME)
	public InvestmentTransaction create(
			@Param(value=InvestmentTransaction.INVEST_ID, required=true,	desc="投资ID") Long invest_id,
			@Param(value=InvestmentTransaction.ASSETS_ID, required=false,	desc="投资品种ID") Long assets_id,
			@Param(value=InvestmentTransaction.USER_ID, required=false,	desc="所属用户ID") Long user_id,
			@Param(value=InvestmentTransaction.SUGGESTION_ID, required=false,	desc="投资建议ID") Long suggestion_id,
			@Param(value=InvestmentTransaction.STRATEGY_ID, required=false,	desc="策略ID") Long strategy_id,
			@Param(value=InvestmentTransaction.TRANSACTION_TYPE, required=true,	desc="交易类型", enumClass=TransactionType.class) Integer transaction_type,
			@Param(value=InvestmentTransaction.TRANSACTION_METHOD, required=false,	desc="交易方法", enumClass=Method.class) Integer transaction_method,
			@Param(value=InvestmentTransaction.OBJECTIVE, required=false,	desc="交易目的", enumClass=Objective.class) Integer objective,
			
			@Param(value=InvestmentTransaction.AMOUNT, required=false,	desc="交易金额") Double amount,
			@Param(value=InvestmentTransaction.QUANTITY, required=false,	desc="交易数量") Double quantity,
			@Param(value=InvestmentTransaction.TAX, required=false,	desc="交易税额") Double tax,
			@Param(value=InvestmentTransaction.PRICE, required=true,	desc="交易价格") Double price,
			@Param(value=InvestmentTransaction.CURRENCY, required=false,	desc="交易币种") String currency,
			
			@Param(value=InvestmentTransaction.TRANSACTION_DATE, required=false,	desc="交易日期") Date transaction_date,
			@Param(value=InvestmentTransaction.NUM, required=false,	desc="投资期数") Integer num,
			@Param(value=InvestmentTransaction.STATUS, required=false,	desc="状态", enumClass=Status.class) Integer status,
			@Param(value=InvestmentTransaction.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=InvestmentTransaction.TERM_TYPE, required=false,	desc="投资周期") Integer term_type,
			@Param(value=InvestmentTransaction.CREATOR, required=false,	desc="创建人") String creator,
			@Param(value=InvestmentTransaction.CREATE_TIME, required=false,	desc="创建时间") Date create_time
		
		) throws Exception {
		if(amount == null && quantity == null) {
			throw new AppException("0101", "交易金额和交易数量不能同时空！");
		}
		Investment v = this.vs.findById(invest_id);
		if(v.getStatus() == Investment.Status.FINISHED.value()) {
			throw new AppException("0101", v.getName()+"["+v.getCode()+"]已经结束，不能再创建交易");
		}
		Principal p = ServiceContext.getContext().getPrincipal();
		if(assets_id == null) assets_id = v.getAssetsId();
		if(user_id == null) user_id = Long.parseLong(p.getId());
		if(transaction_date == null) transaction_date = new Date();
		if(currency == null) currency = v.getCurrency();
		if(term_type == null && strategy_id != null) {
			term_type = this.ss.findById(strategy_id).getStrategyType().getTermType();
		}
		
		InvestmentTransaction t = ModelFactory.create(InvestmentTransaction.class, 
				InvestmentTransaction.ASSETS_ID, assets_id,
				InvestmentTransaction.USER_ID, user_id,
				InvestmentTransaction.TERM_TYPE, term_type,
				InvestmentTransaction.CURRENCY, currency,
				InvestmentTransaction.STATUS, Status.PROCESSING.value,
				InvestmentTransaction.TRANSACTION_DATE,transaction_date,
				InvestmentTransaction.CREATOR, (p == null?null: p.getName()),
				InvestmentTransaction.CREATE_TIME, new Date());
		t = this.recalculate(t, v, amount, quantity, tax, price);
		return t;
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=InvestmentTransaction.ENTITY_NAME)
	public ActionResult update(
			@Param(value=InvestmentTransaction.TRANSACTION_ID, required=true,	desc="ID") Long transaction_id,
			@Param(value=InvestmentTransaction.TRANSACTION_TYPE, required=true,	desc="交易类型", enumClass=TransactionType.class) Integer transaction_type,
			@Param(value=InvestmentTransaction.TRANSACTION_METHOD, required=false,	desc="交易方法", enumClass=Method.class) Integer transaction_method,
			@Param(value=InvestmentTransaction.OBJECTIVE, required=false,	desc="交易目的", enumClass=Objective.class) Integer objective,
			@Param(value=InvestmentTransaction.AMOUNT, required=false,	desc="交易金额") Double amount,
			@Param(value=InvestmentTransaction.QUANTITY, required=false,	desc="交易数量") Double quantity,
			@Param(value=InvestmentTransaction.TAX, required=false,	desc="交易税额") Double tax,
			@Param(value=InvestmentTransaction.PRICE, required=false,	desc="交易价格") Double price,
			@Param(value=InvestmentTransaction.TRANSACTION_DATE, required=false,	desc="交易日期") Date transaction_date,
			@Param(value=InvestmentTransaction.NUM, required=false,	desc="投资期数") Long num,
			@Param(value=InvestmentTransaction.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=InvestmentTransaction.TERM_TYPE, required=false,	desc="投资周期") Integer term_type,
			@Param(value=InvestmentTransaction.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=InvestmentTransaction.UPDATE_TIME, required=false,	desc="修改时间") Date update_time
		) throws Exception {
		if(amount == null && quantity == null) {
			throw new AppException("0101", "交易金额和交易数量不能同时空！");
		}
		//
		InvestmentTransaction t = this.findById(transaction_id);
		if(t.getStatus() != Status.PROCESSING.value()) {
			throw new AppException("0101", "交易已结束，不能修改！");
		}
		//
		Investment v = this.vs.findById(t.getInvestId());
		if(v.getStatus() == Investment.Status.FINISHED.value()) {
			throw new AppException("0101", v.getName()+"["+v.getCode()+"]已经结束，不能再创建交易");
		}
		//
		t = this.recalculate(t, v, amount, quantity, tax, price);
		Principal p = ServiceContext.getContext().getPrincipal();
		return ModelFactory.create(ActionResult.class, 
				InvestmentTransaction.AMOUNT, t.getAmount(),
				InvestmentTransaction.QUANTITY, t.getQuantity(),
				InvestmentTransaction.TAX, t.getTax(),
				InvestmentTransaction.PRICE, t.getPrice(),
				InvestmentTransaction.UPDATOR, (p == null?null: p.getName()),
				InvestmentTransaction.UPDATE_TIME, new Date());
	}


	@MethodInfo("修改")
	@DaoUpdate(entity=InvestmentTransaction.ENTITY_NAME)
	public ActionResult updateForClose(
			@Param(value=InvestmentTransaction.TRANSACTION_ID, required=true,	desc="ID") Long transaction_id,
			@Param(value=InvestmentTransaction.STATUS, required=true, min="1",	desc="状态", enumClass=Status.class) Integer status,
			@Param(value=InvestmentTransaction.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=InvestmentTransaction.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=InvestmentTransaction.UPDATE_TIME, required=false,	desc="修改时间") Date update_time
		) {
		return null;
	}

	@MethodInfo("修改")
	@Transaction
	@DaoUpdate(entity=InvestmentTransaction.ENTITY_NAME)
	public ActionResult finishTransaction(
		
			@Param(value=InvestmentTransaction.TRANSACTION_ID, required=true,	desc="ID") Long transaction_id,
			@Param(value=InvestmentTransaction.TRANSACTION_METHOD, required=false,	desc="交易方法", enumClass=Method.class) Integer transaction_method,
			@Param(value=InvestmentTransaction.OBJECTIVE, required=false,	desc="交易目的", enumClass=Objective.class) Integer objective,
			@Param(value=InvestmentTransaction.AMOUNT, required=true,	desc="交易金额") Double amount,
			@Param(value=InvestmentTransaction.QUANTITY, required=false,	desc="交易数量") Double quantity,
			@Param(value=InvestmentTransaction.TAX, required=false,	desc="交易税额") Double tax,
			@Param(value=InvestmentTransaction.PRICE, required=true,	desc="交易价格") Double price,
			@Param(value=InvestmentTransaction.CURRENCY, required=false,	desc="交易币种") String currency,
			@Param(value=InvestmentTransaction.CURRENCY_RATE, required=false,	desc="交易汇率") Double currency_rate,
			
			@Param(value=InvestmentTransaction.TRANSACTION_DATE, required=false,	desc="交易日期") Date transaction_date,
			@Param(value=InvestmentTransaction.TRANSACTION_FINISH_TIME, required=false,	desc="交易完成时间") Date transaction_finish_time,
			@Param(value=InvestmentTransaction.NUM, required=false,	desc="投资期数") Long num,
			@Param(value=InvestmentTransaction.STATUS, required=true, min="1",	desc="状态", enumClass=Status.class) Integer status,
			@Param(value=InvestmentTransaction.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=InvestmentTransaction.TERM_TYPE, required=false,	desc="投资周期") Integer term_type,
			@Param(value=InvestmentTransaction.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=InvestmentTransaction.UPDATE_TIME, required=false,	desc="修改时间") Date update_time
			
		) throws Exception {
		if(amount == null && quantity == null) {
			throw new AppException("0101", "交易金额和交易数量不能同时空！");
		}
		//
		InvestmentTransaction t = this.findById(transaction_id);
		if(t.getStatus() != Status.PROCESSING.value()) {
			throw new AppException("0101", "交易已结束，不能修改！");
		}
		//
		Investment v = vs.findById(t.getInvestId());
		if(v.getStatus() == Investment.Status.FINISHED.value()) {
			throw new AppException("0101", v.getName()+"["+v.getCode()+"]已经结束，不能修改！");
		}
		//
		if(status == Status.SUCCESS.value()) {
			//账户
			if(t.getTransactionType() == TransactionType.BUY.value()) {
				this.as.transfermOutForBuy(v.getAccountId(), amount, remarks, t.getTransactionId());
			} else {
				this.as.transfermInForSell(v.getAccountId(), amount, remarks, t.getTransactionId());
			}
			//投资
			this.vs.updateByTransaction(t.getInvestId(), t.getTransactionType(), amount, quantity, tax, price);
			//建议
			if(t.getSuggestionId() != null) {
				this.iss.updateTransactionStatus(t.getSuggestionId(), InvestmentSuggestion.Status.INVESTED.value(), 
						t.getTransactionId(), new Date(), null, null);
			}
			//策略
			if(t.getStrategyId() != null) {
				this.ss.transactionCallback(t.getStrategyId(), transaction_id, Status.SUCCESS.value(), t.getTransactionType(), 
						quantity, price, amount);
			}
		} else {
			if(t.getSuggestionId() != null) {
				this.iss.updateTransactionStatus(t.getSuggestionId(), InvestmentSuggestion.Status.FAILED.value(), 
						t.getTransactionId(), new Date(), null, null);
			}
		}
		if(currency_rate == null) {
			if(currency == null) currency = t.getCurrency();
			if(currency == null) currency = v.getCurrency();
			currency_rate = this.cs.findByCode(currency).getRate();
		}
		//
		t = this.recalculate(t, v, amount, quantity, tax, price);
		Principal p = ServiceContext.getContext().getPrincipal();
		return ModelFactory.create(ActionResult.class, 
				InvestmentTransaction.AMOUNT, t.getAmount(),
				InvestmentTransaction.QUANTITY, t.getQuantity(),
				InvestmentTransaction.TAX, t.getTax(),
				InvestmentTransaction.PRICE, t.getPrice(),
				InvestmentTransaction.CURRENCY, currency,
				InvestmentTransaction.CURRENCY_RATE, currency_rate,
				InvestmentTransaction.TRANSACTION_FINISH_TIME, new Date(),
				
				InvestmentTransaction.UPDATOR, (p == null?null: p.getName()),
				InvestmentTransaction.UPDATE_TIME, new Date());
	}
	


	@MethodInfo("修改")
	@DaoUpdate(entity=InvestmentTransaction.ENTITY_NAME)
	public ActionResult appendTransaction(
			@Param(value=InvestmentTransaction.TRANSACTION_ID, required=true,	desc="ID") Long transaction_id,
			@Param(value=InvestmentTransaction.TRANSACTION_METHOD, required=false,	desc="交易方法", enumClass=Method.class) Integer transaction_method,
			@Param(value=InvestmentTransaction.OBJECTIVE, required=false,	desc="交易目的", enumClass=Objective.class) Integer objective,
			@Param(value=InvestmentTransaction.TERM_TYPE, required=false,	desc="投资周期") Integer term_type,
			@Param(value=InvestmentTransaction.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=InvestmentTransaction.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=InvestmentTransaction.UPDATE_TIME, required=false,	desc="修改时间") Date update_time
			
		) throws Exception {
		Principal p = ServiceContext.getContext().getPrincipal();
		return ModelFactory.create(ActionResult.class, 
				InvestmentTransaction.UPDATOR, (p == null?null: p.getName()),
				InvestmentTransaction.UPDATE_TIME, new Date());
	}
	
	private InvestmentTransaction recalculate(InvestmentTransaction t, Investment v,
			@Param(value=InvestmentTransaction.AMOUNT, required=true,	desc="交易金额") Double amount,
			@Param(value=InvestmentTransaction.QUANTITY, required=false,	desc="交易数量") Double quantity,
			@Param(value=InvestmentTransaction.TAX, required=false,	desc="交易税额") Double tax,
			@Param(value=InvestmentTransaction.PRICE, required=true,	desc="交易价格") Double price) {
		if(amount == null && quantity == null) {
			throw new AppException("0101", "交易金额和交易数量不能同时空！");
		}
		if(price == null) {
			price = v.getAssets().getPrice();
		}
		if(amount == null) {
			amount = quantity * price;
		}
		if(quantity == null) {
			quantity = amount / price;
		}
		if(tax == null) {
			tax = amount * v.getAssets().getTaxRate();
		}
		t.setAmount(amount);
		t.setQuantity(quantity);
		t.setTax(tax);
		t.setPrice(price);
		return t;
	}

	@Transaction
	public ActionResult closeAllActivesByInvestId(
			@Param(value=Strategy.INVEST_ID, required=true,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=Strategy.UPDATE_TIME, required=false,	desc="修改时间") Date update_time) {
		List<InvestmentTransaction> list = this.findAll(null, invest_id, null, null, null, 
				new Integer[] {Status.PROCESSING.value}, null, null, 0, 10000).getRows();
		list.stream().forEach(x -> {
			this.updateForClose(x.getTransactionId(), Status.FAILED.value, "Closed for investment!", updator, update_time);
		});
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@MethodInfo("删除")
	@DaoRemove(entity=InvestmentTransaction.ENTITY_NAME)
	public ActionResult remove(
			@Param(value=InvestmentTransaction.TRANSACTION_ID, required=true,	desc="ID") Long transaction_id
		) {
		InvestmentTransaction t = this.findById(transaction_id);
		if(t.getStatus() != Status.PROCESSING.value()) {
			throw new AppException("0101", "交易已结束，不能删除！");
		}
		if(t.getSuggestionId() != null) {
			this.iss.updateTransactionStatus(t.getSuggestionId(), InvestmentSuggestion.Status.PREPARING.value(), null, null, null, new Date());
		}
		return null;
	}

	@MethodInfo("删除")
	@Transaction
	public ActionResult removeByInvestId(
			@Param(value=InvestmentTransaction.INVEST_ID, required=true,	desc="投资ID") Long invest_id
		) throws Exception {
		List<InvestmentTransaction> list = this.findAll(null, invest_id, null, null, null, null, null, null, 0, Integer.MAX_VALUE).getRows();
		list.stream().forEach(x -> {
			this.remove(x.getTransactionId());
		});
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("搜索")
	@DaoQuery(entity=InvestmentTransaction.ENTITY_NAME, pageable=true, modelClass=InvestmentTransaction.class,
		qsql="select * from (select t.*,s.type_id as strategy_type_id from i_investment_transaction t left join i_strategy s on t.strategy_id=s.strategy_id) t",
		csql="select count(1) as c from (select t.*,s.type_id as strategy_type_id from i_investment_transaction t left join i_strategy s on t.strategy_id=s.strategy_id) t",
		osql="ORDER BY create_time desc")
	public QueryResult<InvestmentTransaction> findAll(
			@Param(value=InvestmentTransaction.USER_ID, required=false,	desc="所属用户ID") Long user_id,
			@Param(value=InvestmentTransaction.INVEST_ID, required=false,	desc="投资ID") Long invest_id,
			@Param(value=InvestmentTransaction.TRANSACTION_TYPE, required=false,	desc="交易类型", enumClass=TransactionType.class) Integer transaction_type,
			@Param(value=InvestmentTransaction.TRANSACTION_METHOD, required=false,	desc="交易方法", enumClass=Method.class) Integer transaction_method,
			@Param(value=InvestmentTransaction.OBJECTIVE, required=false,	desc="交易目的", enumClass=Objective.class) Integer objective,
			@Param(value=InvestmentTransaction.STATUS, required=false,	desc="状态", enumClass=Status.class, tag="IN") Integer[] status,
			@Param(value=InvestmentTransaction.TRANSACTION_FINISH_TIME, required=false,	desc="交易日期", tag="BETWEEN") Date[] transaction_date,
			@Param(value="strategy_type_id", required=false,	desc="投资策略ID") Long strategy_type_id,
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		Principal p = ServiceContext.getContext().getPrincipal();
		if(user_id == null && p != null) {
			user_id = Long.parseLong(p.getId());
		}
		return ModelFactory.create(QueryResult.class, InvestmentTransaction.USER_ID, user_id);
	}

	@MethodInfo("查询")
	@DaoQuery(entity=InvestmentTransaction.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public InvestmentTransaction findById(
			@Param(value=InvestmentTransaction.TRANSACTION_ID, required=true,	desc="ID") Long transaction_id
		) {
		return null;
	}

}